using System;
using System.Collections.Generic;
using System.Text;
using BDDfyIt.CodeDom;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using TestStack.BDDfy.Core;
using TestStack.BDDfy.Scanners.StepScanners.ExecutableAttribute.GwtAttributes;

namespace BDDfyIt.Core
{
    public class TextStoryBDDfyItCodeGenerator : IBDDfyItCodeGenerator<TextStory>
    {
        private string _outputFile;

        public void BDDfyIt(TextStory storyModel, string outputFile, string outputNamespace = null, string outputClassName = null)
        {
            Validate(storyModel, outputFile);
            TextStoryExtensions.StoryModel = storyModel;
            var codeDomWrapper = new CodeDomWrapper();

            codeDomWrapper.CreateNamespace(TextStoryExtensions.GetNamespace(outputNamespace));
            codeDomWrapper.CreateImports(new List<string>
                                             {
                                                 "System",
                                                 "Microsoft.VisualStudio.TestTools.UnitTesting",
                                                 "TestStack.BDDfy",
                                                 "TestStack.BDDfy.Core",
                                                 "TestStack.BDDfy.Scanners.StepScanners.ExecutableAttribute.GwtAttributes"
                                             });
            foreach (var scenario in storyModel.Scenarios)
            {
                var bddClassName = TextStoryExtensions.GetClassName(scenario.Name, outputClassName);
                codeDomWrapper.CreateClass(bddClassName, "public");
                var storyAttributeArguments = new List<string>();
                var storyAttributeArgumentValues = new List<string>();
                TextStoryExtensions.SetStoryAttributeValue(out storyAttributeArguments, out storyAttributeArgumentValues);
                codeDomWrapper.CreateCustomAttributeOnClass(typeof(StoryAttribute), storyAttributeArguments,
                                                            storyAttributeArgumentValues);

                codeDomWrapper.CreateVoidMethod("Setup", "public");
                codeDomWrapper.AddCommentToMethod("This is setup method. Put your setup code here.");
                codeDomWrapper.AddMethodToClass();

                foreach (var given in scenario.Givens)
                {
                    var type = scenario.Givens.IndexOf(given) == 0 ? "Given" : "AndGiven";
                    TextStoryExtensions.AddMethodToClass(codeDomWrapper, type, given);
                    codeDomWrapper.AddCommentToMethod("These methods will have your initialization steps");
                }

                foreach (var when in scenario.Whens)
                {
                    var type = scenario.Whens.IndexOf(when) == 0 ? "When" : "AndWhen";
                    TextStoryExtensions.AddMethodToClass(codeDomWrapper, type, when);
                    codeDomWrapper.AddCommentToMethod("These methods will have your execution steps");
                }

                foreach (var then in scenario.Thens)
                {
                    var type = scenario.Thens.IndexOf(then) == 0 ? "Then" : "AndThen";
                    TextStoryExtensions.AddMethodToClass(codeDomWrapper, type, then);
                    codeDomWrapper.AddCommentToMethod("These methods will have your assert steps");
                }

                codeDomWrapper.CreateVoidMethod("TearDown", "public");
                codeDomWrapper.AddCommentToMethod("This is a teardown method");
                codeDomWrapper.AddMethodToClass();

                codeDomWrapper.AddClassToNameSpace();

                codeDomWrapper.CreateClass("BDDfyStoryRunner", "public");
                codeDomWrapper.CreateCustomAttributeOnClass(typeof(TestClassAttribute));
                codeDomWrapper.AddCommentToClass("This runner will run your scenario");
                codeDomWrapper.CreateVoidMethod("Execute", "public");
                codeDomWrapper.AddExecuteCodeToMethod(bddClassName);
                codeDomWrapper.CreateCustomAttributeOnMethod(typeof(TestMethodAttribute));
                codeDomWrapper.AddMethodToClass();

                codeDomWrapper.AddClassToNameSpace();
            }

            codeDomWrapper.GenerateCSharpCode(_outputFile);
        }

        private void Validate(TextStory storyModel, string outputFile)
        {
            if (storyModel == null)
            {
                throw new Exception("Cannot parse an empty story");
            }
            if (string.IsNullOrEmpty(outputFile))
            {
                outputFile = string.Format(@"{0}\Out.cs", Environment.CurrentDirectory);
            }
            _outputFile = outputFile;
        }
    }
}